/*
 * Copyright (c) 2011 Sungkyu kim, rushprint@gmail.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package skkim.jmulticast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.List;

import joptsimple.OptionParser;
import joptsimple.OptionSet;

public class JMulticast {

	private static void printUsage() {
		System.out.println("\nUsage> JMulticast [options] ip port filename");
		System.out.println("\ncommmon options:");
		System.out.println("\t-s     : set receive & save mode. default is cast mode.");
		System.out.println("\t-b nnn : buffer size. default=2048");
		System.out.println("\ncast mode options:");
		System.out.println("\t-r     : enable repeat");
		System.out.println("\t-i nnn : cast interval msec. default 10 msec.");
		System.out.println("\nreceive & save mode options:");
		System.out.println("\t-l     : mega bytes to save. default 10 MB");
		System.out.println();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		OptionParser parser = new OptionParser( "rsb:i:l:" );

		try
		{
			OptionSet options = parser.parse(args);

			List<String> others = options.nonOptionArguments();

			//System.out.println(others);

			if(others.size() < 3)
				throw new Exception("Need more arguments");

			String ip = others.get(0);
			Integer port = Integer.valueOf(others.get(1));
			String filename = others.get(2);

			JMulticast multicast = new JMulticast();

			Integer bufferSize = 2048;
			if(options.has("b")) {
				bufferSize = Integer.valueOf((String) options.valueOf("b"));
				System.out.println("buffer size : " + bufferSize);
			} else
				System.out.println("Use default buffer size : " + bufferSize);

			if(options.has("s")) {

				Long maxSize = Long.valueOf(10);
				if(options.has("l")) {
					maxSize = Long.valueOf((String) options.valueOf("l"));
					System.out.println("receiving " + maxSize + "MB");
				}

				System.out.println(String.format("Receiving: %s:%d -> %s", ip, port, filename));
				multicast.startReceiver(ip, port, filename, bufferSize, maxSize);
			} else {

				//	repeat or not.
				boolean repeat = options.has("r");

				if(repeat)
					System.out.println("Repeat mode on");

				Integer interval = 10;
				if(options.has("i")) {
					interval = Integer.valueOf((String) options.valueOf("i"));
					System.out.println("set cast interval : " + interval);
				}

				System.out.println(String.format("Casting: %s -> %s:%d", filename, ip, port));
				multicast.startServer(ip, port, filename, bufferSize, repeat, interval);
			}

			System.out.println("\ndone.\n");

		}
		catch (Exception e)
		{
			System.out.println("ERROR!! " + e.getMessage());
			printUsage();
		}

	}

	private void startServer(String ip, Integer port, String filename, int bufferSize, boolean repeat, Integer interval) {

		long progress = 0;

		File file = new File(filename);

		byte[] buffer = new byte[bufferSize];

		try {
			FileInputStream input = new FileInputStream(file);

			InetAddress ipaddr = InetAddress.getByName(ip);

			MulticastSocket s = new MulticastSocket();

			while(true) {
				int readBytes = input.read(buffer);

				if(readBytes == -1) {	//	eof -> reopen.
					if(!repeat)
						return;

					input.close();
					input = null;
					input = new FileInputStream(file);
					continue;
				}

				progress += readBytes;

				DatagramPacket pack = new DatagramPacket(
						buffer, readBytes, ipaddr, port);

				s.send(pack);

				if((progress % (1024 * 1024)) == 0)
					System.out.println(String.format("%d bytes casted", progress));


				try {
					Thread.sleep(interval);
				} catch (InterruptedException e) {
				}

			}

		} catch (FileNotFoundException e) {
			System.out.println("ERROR!! " + e.getMessage());
		} catch (IOException e) {
			System.out.println("ERROR!! " + e.getMessage());
		}

	}

	private void startReceiver(String ip, Integer port, String filename, int bufferSize, Long maxSize) {

		long progress = 0;

		byte[] buffer = new byte[bufferSize];

		File file = new File(filename);

		MulticastSocket s;
		try {
			FileOutputStream output = new FileOutputStream(file);

			s = new MulticastSocket(port);
			s.joinGroup(InetAddress.getByName(ip));

			while(true) {
				DatagramPacket pack = new DatagramPacket(buffer, buffer.length);
				s.receive(pack);

				output.write(pack.getData());

				progress += pack.getLength();

				if((progress % (1024 * 1024)) == 0)
					System.out.println(String.format("%d bytes received.", progress));

				if(progress >= maxSize * 1024 * 1024)
					break;
			}

			s.leaveGroup(InetAddress.getByName(ip));
			s.close();
		} catch (IOException e) {
			System.out.println("ERROR!! " + e.getMessage());
		}
	}

}
